from point import Point, UnitCirclePoint
import math

#Class that holds a set of vertices.
class Arrangement(object):
    def __init__(self, name = ""):
        self._name = name
        self._arr = []
        self._last = None
    def __str__(self):
        name = " \"" + self.name + "\"" if self.name != "" else ""
        return "<" + self.__class__.__name__ + "%s [%d]:\n" % (name, len(self)) + "\n".join(["  " + str(x) for x in self]) + "\n>"
    def __repr__(self):
        name = " \"" + self.name + "\"" if self.name != "" else ""
        return "<" + self.__class__.__name__ + "%s [%d]>" % (name, len(self))
    def __getitem__(self, index):
        return self._arr[index]
    def add(self, point):
        assert isinstance(point, Point)
        if (self._last != None):
            self._last.setNext(point)
        self._arr.append(point)
        point.setNext(self._arr[0])
        self._last = point
    def __len__(self):
        return len(self._arr)
    def __iter__(self):
        class ArrangementIter(object):
            def __init__(self, arrangement):
                self._arrangement = arrangement
                self._index = -1
            def __iter__(self):
                return self
            def next(self):
                if self._index < len(self._arrangement) - 1:
                    self._index += 1
                    p = self._arrangement[self._index]
                    assert isinstance(p, Point)
                    return p
                else:
                    raise StopIteration()
        return ArrangementIter(self)
    def __contains__(self, item):
        return item in self._arr
    @property
    def name(self):
        return self._name
    @property
    def center(self):
        center = Point(0, 0)
        for p in self:
            center = center + p
        center = center / len(self)
        return center
    def arrangeOnUnitCircle(self):
        #arranges the vertices of a unit circle
        arr = []
        center = self.center
        for p in self:
            arr.append(LinkedPoint((p - center).unit_point, p, self))
        arr.sort()
        
        circle = Arrangement(self.name)
        for item in arr:
            circle.add(item)

        return circle

        #assuming it was orderd in the first place:

        #ordered_circle = Arrangement(self.name)
        #next = min(circle)
        #last = next.previous
        #for index in xrange(len(circle) - 1):
        #    next = next.next
        #    ordered_circle.add(next.previous)
        #ordered_circle.add(last)
        #return ordered_circle

    @staticmethod
    def combine(arr1, arr2):
        #combines two arrangements into one (ordrerly)
        assert isinstance(arr1, Arrangement) and isinstance(arr2, Arrangement)
        combined = Arrangement()
        index1 = 0
        index2 = 0
        while ((index1 < len(arr1)) and (index2 < len(arr2))):
            p1, p2 = arr1[index1], arr2[index2]
            if (p1.angle < p2.angle):
                combined.add(LinkedPoint(p1, p1, arr1))
                index1 += 1
            else:
                combined.add(LinkedPoint(p2, p2, arr2))
                index2 += 1
        while (index1 < len(arr1)):
            p1 = arr1[index1]
            combined.add(LinkedPoint(p1, p1, arr1))
            index1 += 1
        while (index2 < len(arr2)):
            p2 = arr2[index2]
            combined.add(LinkedPoint(p2, p2, arr2))
            index2 += 1

        return combined

#holds a vertice and it's original point
class LinkedPoint(Point):
    def __init__(self, value, original, arrangement):
        assert isinstance(original, Point)
        assert isinstance(arrangement, Arrangement)
        Point.__init__(self, value.x, value.y)
        self._original = original
        self._arrangement = arrangement
    @property
    def original(self):
        return self._original
    @property
    def arrangement(self):
        return self._arrangement
    @property
    def tag(self):
        return self.arrangement.name
